using System;

namespace ntools.NetduinoGo.PlusModule.NetduinoGo.Framework
{
    public class Socket : IDisposable
    {
        private byte instance;

        public Socket(System.Net.Sockets.AddressFamily af, System.Net.Sockets.SocketType st, System.Net.Sockets.ProtocolType pt)
        {
            this.instance = Plus.Link.Call(Link.INSTANCE_MAIN, Link.COMMAND_MAIN_CREATESOCKET, new byte[] { (byte)(int)af, (byte)(int)st, (byte)(int)pt })[0];
        }

        public Socket(byte instance)
        {
            this.instance = instance;
        }

        public void Connect(System.Net.IPEndPoint remoteendpoint)
        {
            byte[] addressbytes = remoteendpoint.Address.GetAddressBytes();
            byte[] buffer = new byte[addressbytes.Length + 2];

            buffer[0] = this.instance;
            buffer[1] = (byte)remoteendpoint.Port;
            Array.Copy(addressbytes, 0, buffer, 2, addressbytes.Length);

            Plus.Link.Call(Link.INSTANCE_SOCKET, Link.COMMAND_SOCKET_CONNECT, buffer);
        }

        public void SetSocketOption(System.Net.Sockets.SocketOptionLevel sol, System.Net.Sockets.SocketOptionName son, bool value)
        {
            Plus.Link.Call(Link.INSTANCE_SOCKET, Link.COMMAND_SOCKET_SETSOCKETOPTION, new byte[] { instance, (byte)(int)sol, (byte)(int)son, (value) ? (byte)1 : (byte)0 });
        }

        public void Send(byte[] data)
        {
            byte[] buffer = new byte[data.Length + 1];
            buffer[0] = instance;
            Array.Copy(data, 0, buffer, 1, data.Length);
            Plus.Link.Call(Link.INSTANCE_SOCKET, Link.COMMAND_SOCKET_SEND, buffer);
        }

        public int SendTimeout
        {
            get
            {
                return (int)Plus.Link.Call(Link.INSTANCE_SOCKET, Link.COMMAND_SOCKET_SENDTIMEOUT, new byte[] { instance })[0];
            }
            set
            {
                Plus.Link.Call(Link.INSTANCE_SOCKET, Link.COMMAND_SOCKET_SENDTIMEOUT, new byte[] { instance, (byte)value });
            }
        }

        public void Bind(System.Net.IPEndPoint endpoint)
        {
            byte[] addressbytes = endpoint.Address.GetAddressBytes();
            byte[] buffer = new byte[addressbytes.Length + 2];
            buffer[0] = this.instance;
            buffer[1] = (byte)endpoint.Port;
            Array.Copy(addressbytes, 0, buffer, 2, addressbytes.Length);

            Plus.Link.Call(Link.INSTANCE_SOCKET, Link.COMMAND_SOCKET_BIND, buffer);
        }

        public void Listen(int num)
        {
            byte[] buffer = new byte[2];
            buffer[0] = this.instance;
            buffer[1] = (byte)num;

            Plus.Link.Call(Link.INSTANCE_SOCKET, Link.COMMAND_SOCKET_LISTEN, buffer);
        }


        public byte Instance
        {
            get
            {
                return this.instance;
            }
        }

        public void Dispose()
        {
            Plus.Link.Call(Link.INSTANCE_SOCKET, Link.COMMAND_SOCKET_DISPOSE, new byte[] { instance });
        }

        public bool Poll(int microseconds, System.Net.Sockets.SelectMode sm)
        {
            return 1 == Plus.Link.Call(Link.INSTANCE_SOCKET, Link.COMMAND_SOCKET_POLL, new byte[] { this.instance, (byte)microseconds, (byte)(int)sm })[0];
        }

        public Socket Accept()
        {
            return new Socket(Plus.Link.Call(Link.INSTANCE_SOCKET, Link.COMMAND_SOCKET_ACCEPT, new byte[] { this.instance })[0]);
        }

        public int Available
        {
            get
            {
                return Plus.Link.Call(Link.INSTANCE_SOCKET, Link.COMMAND_SOCKET_AVAILABLE, new byte[] { this.instance })[0];
            }
        }

        public int Receive(byte[] buffer, int length, System.Net.Sockets.SocketFlags sf)
        {
            byte[] res = Plus.Link.Call(Link.INSTANCE_SOCKET, Link.COMMAND_SOCKET_RECEIVE, new byte[] { this.instance, (byte)length, (byte)(int)sf });
            Array.Copy(res, 0, buffer, 0, res.Length);
            return res.Length;
        }

        public System.Net.EndPoint RemoteEndpoint
        {
            get
            {
                byte[] res = Plus.Link.Call(Link.INSTANCE_SOCKET, Link.COMMAND_SOCKET_REMOTEENDPOINT, new byte[] { this.instance });
                byte[] address = new byte[res.Length - 1];
                Array.Copy(res, 1, address, 0, address.Length);
                return new System.Net.IPEndPoint(new System.Net.IPAddress(address), res[0]);
            }
        }
    }
}
